#pragma once

#include "../umlmodel_global.h"

#include <memory>
#include <vector>

#include "../umlcore/PartialElement.h"

using std::make_shared;
using std::shared_ptr;
using std::vector;

namespace uml {
class Constraint;
class ElementImport;
class NamedElement;
class PackageImport;
class PackageableElement;

/// <summary>
/// Namespace
/// A Namespace is an Element in a model that owns and/or imports a set of
/// NamedElements that can be identified by name.
/// </summary>
class UMLMODEL_EXPORT Namespace : public PartialElement {

public: // JsonSerializable override
    void fromJson(const QJsonObject& json, QStringList& errorList) override;
    void toJson(QJsonObject& json) override;

public: // Methods
    /// <summary>
    /// excludeCollisions
    /// The query excludeCollisions() excludes from a set of PackageableElements any
    /// that would not be distinguishable from each other in this Namespace.
    /// </summary>
    virtual shared_ptr<vector<PackageableElement>> excludeCollisions(vector<shared_ptr<PackageableElement>> imps) const;

    /// <summary>
    /// getNamesOfMembers
    /// The query getNamesOfMember() gives a set of all of the names that a member would
    /// have in a Namespace, taking importing into account. In general a member can have
    /// multiple names in a Namespace if it is imported more than once with different
    /// aliases.
    /// </summary>
    virtual shared_ptr<vector<QString>> getNamesOfMembers(shared_ptr<NamedElement> element) const;

    /// <summary>
    /// importMembers
    /// The query importMembers() defines which of a set of PackageableElements are
    /// actually imported into the Namespace. This excludes hidden ones, i.e., those
    /// which have names that conflict with names of ownedMembers, and it also excludes
    /// PackageableElements that would have the indistinguishable names when imported.
    /// </summary>
    virtual shared_ptr<vector<PackageableElement>> importMembers(vector<shared_ptr<PackageableElement>> imps) const;

    /// <summary>
    /// membersAreDistinguishable
    /// The Boolean query membersAreDistinguishable() determines whether all of the
    /// Namespace's members are distinguishable within it.
    /// </summary>
    virtual bool membersAreDistinguishable() const;

public: // Properties
    /// <summary>
    /// elementImports getter
    /// References the ElementImports owned by the Namespace.
    /// </summary>
    vector<shared_ptr<ElementImport>>& elementImports();

    /// <summary>
    /// importedMembers getter
    /// References the PackageableElements that are members of this Namespace as a
    /// result of either PackageImports or ElementImports.
    /// </summary>
    vector<shared_ptr<PackageableElement>>& importedMembers();

    /// <summary>
    /// members getter
    /// A collection of NamedElements identifiable within the Namespace, either by being
    /// owned or by being introduced by importing or inheritance.
    /// </summary>
    vector<shared_ptr<NamedElement>>& members();

    /// <summary>
    /// ownedMembers getter
    /// A collection of NamedElements owned by the Namespace.
    /// </summary>
    vector<shared_ptr<NamedElement>>& ownedMembers();

    /// <summary>
    /// ownedRules getter
    /// Specifies a set of Constraints owned by this Namespace.
    /// </summary>
    vector<shared_ptr<Constraint>>& ownedRules();

    /// <summary>
    /// packageImports getter
    /// References the PackageImports owned by the Namespace.
    /// </summary>
    vector<shared_ptr<PackageImport>>& packageImports();

private: // Fields
    /// <summary>
    /// elementImports
    /// References the ElementImports owned by the Namespace.
    /// </summary>
    vector<shared_ptr<ElementImport>> elementImports_;

    /// <summary>
    /// importedMembers
    /// References the PackageableElements that are members of this Namespace as a
    /// result of either PackageImports or ElementImports.
    /// </summary>
    vector<shared_ptr<PackageableElement>> importedMembers_;

    /// <summary>
    /// members
    /// A collection of NamedElements identifiable within the Namespace, either by being
    /// owned or by being introduced by importing or inheritance.
    /// </summary>
    vector<shared_ptr<NamedElement>> members_;

    /// <summary>
    /// ownedMembers
    /// A collection of NamedElements owned by the Namespace.
    /// </summary>
    vector<shared_ptr<NamedElement>> ownedMembers_;

    /// <summary>
    /// ownedRules
    /// Specifies a set of Constraints owned by this Namespace.
    /// </summary>
    vector<shared_ptr<Constraint>> ownedRules_;

    /// <summary>
    /// packageImports
    /// References the PackageImports owned by the Namespace.
    /// </summary>
    vector<shared_ptr<PackageImport>> packageImports_;

};

} // namespace uml
